#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
@File  : uer_item_based_simple_improved_03.py
@author: Jiangwei
@Time  : 2023/04/23 9:47
@function:

在 uer_item_based_simple_improved_02.py的基础上修改成：
1、某一个用户把电影相似列表做成一个函数，通过用户id调用，方便调用；

"""
import numpy as np
from sklearn.metrics.pairwise import cosine_similarity
from collections import Counter

#--------------------1、获取数据  --------------------------------
# 1. 导入pymysql
import pymysql

# 2. 创建连接对象，连接mysql数据库
con=pymysql.connect(host='localhost',password='123456',port=3306,user='root',charset='utf8')

# 3. 创建游标对象
cur=con.cursor()

# 4、执行SQL命令
cur.execute("use sakila;")
sql = "select rental.customer_id as a, inventory.film_id as b from rental, inventory where rental.inventory_id = inventory.inventory_id order by a;"
cur.execute(sql)
all=cur.fetchall()#返回所有记录

data = {}
for item in all:
    k = 'user_' + str(item[0]) #租赁用户id
    v = 'film_' + str(item[1]) #电影id
    if k not in data.keys():
        data[k]={v}
    else:
        data[k].add(v)

# 6、关闭游标
cur.close()

# 7、关闭连接
con.close()



#--------------------2、余弦相似度  --------------------------------
# 2.1把所有电影转化成向量，参考词袋模型的思想
all_films_list_duplicated = []
all_users = []
for u,f in data.items():
    all_users.append(u)
    all_films_list_duplicated.extend(list(f))
all_films_set = set(all_films_list_duplicated)
#去重后转成构造电影词典
films2id = {} #注意这里的电影的film2id为什么又要给电影编一个id?因为数据里的id不一定都是顺序且连续的，这里的id是为了后续方便矩阵运算，构造用户-电影矩阵
id2films = {}
all_films_list = list(all_films_set)
for i in range(len(all_films_list)):
    films2id[all_films_list[i]]=i
    id2films[i] = all_films_list[i]

# 2.2 构造用户-电影矩阵
# 初始化 用户-电影  矩阵：用户是按all_users出现的顺序放；电影列是按film2id编的id对应位置设为1
user_film_matrix = np.zeros(shape=[len(all_users), len(all_films_list)])
for u_i in range(len(all_users)):
    for f in data[all_users[u_i]]:
        user_film_matrix[u_i, films2id[f]]=1

# 计算 用户- 电影 相似矩阵 （使用sklearn里的cosine_similarity函数）
# 余弦相似度矩阵：如599个用户，这里就是一个599x599的矩阵，对角线上是自己对自己的相似性
user_similarity_matrix = cosine_similarity(user_film_matrix)


#--------------------3、获取每个用户的推荐电影集合  --------------------------------
# 定义N=10，返回的是N个最相似用户的电影的交集
N=10

user_sim_most_N_dict = {} #每个用户最相似的N个用户（余弦相似度计算出来的）
for u_i in range(user_similarity_matrix.shape[0]):
    sim_max_N = np.argsort(-user_similarity_matrix[u_i])[1:(N+1)] #‘-’表示从大到小排序，返回索引位置，即最相似的K个用户
    user_sim_most_N_dict[u_i] = sim_max_N
#注意user_sim_most_N_dict里的id都是用户-电影里的顺序，和all_users里出现的顺序是一样的。

for current_user_i in user_sim_most_N_dict.keys():

    # 获取所有N个最相似用户的并集，然后取前M个出现次数最多的
    films_union = []
    for s_u_i in user_sim_most_N_dict[current_user_i]:
        # s_u_i: 前最相似的用户id; all_users[s_u_i]:由索引转化为用户的原始名，去掉“user_”即为数据库里的用户id； data[all_users[s_u_i]]:该相似用户的电影列表
        films_union.extend(list(data[all_users[s_u_i]]))



    #从并集中剔除当前用户看过的电影
    for item in data[all_users[current_user_i]]:
        while item in films_union:
            films_union.remove(item)

    collection_films = Counter(films_union)
    # print(collection_films)
    # 还可以输出频率最大的n个元素, 类型为list
    most_counterNum = collection_films.most_common(5)
    # 为了给java调用，这里注释掉
    #print('和{}（当前用户）最相似的N个用户看过的电影中还没看过的有：'.format(all_users[current_user_i]))
    #print(most_counterNum)



    #如果做成服务，请返回该列表
    #略，参照实验三调用返回那次实验，自己实现


#把上面的循改在成函数,共java系统调用
def getSimilarityById(db_uid):

    #把数据库的用户id转化成上面计算相识时一致的标识方法，即加'user_'
    current_user = 'user_' + str(db_uid)
    # print(current_user)

    #判断改用户是否已经计算过相似性
    if current_user in all_users:
        #获取索引位置
        current_user_i = all_users.index(current_user)
        # 获取所有N个最相似用户的并集，然后取前M个出现次数最多的
        films_union = []
        for s_u_i in user_sim_most_N_dict[current_user_i]:
            # s_u_i: 前最相似的用户id; all_users[s_u_i]:用户名； data[all_users[s_u_i]]:该相似用户的电影列表
            films_union.extend(list(data[all_users[s_u_i]]))

        # 从并集中剔除当前用户看过的电影
        for item in data[all_users[current_user_i]]:
            while item in films_union:
                films_union.remove(item)

        collection_films = Counter(films_union)
        # print(collection_films)
        # 还可以输出频率最大的n个元素, 类型为list
        most_counterNum = collection_films.most_common(5)

        #print('和{}（当前用户）最相似的N个用户看过的电影中还没看过的有：'.format(all_users[current_user_i]))
        #print(most_counterNum)

        #返回相似李彪
        result = '-'.join([item[0].replace('film_','')  for item in most_counterNum])
        # print(result)
        return  result

    else:
        #没有计算过相似性时，应该定期计算相似性，这里占时可以返回最流行的几部电影，这里略掉，
        #这里暂时任意写死了，请同学们自己后续更改
        return '468-518-305-502-510'
        pass








if __name__ == '__main__':

    # print(getSimilarityById(str(1)))

    # 用java调用时用下面的代码：
    import sys
    print(getSimilarityById(int(sys.argv[1])))
